package tech.waterism.model;

import cn.hutool.core.io.resource.ResourceUtil;
import tech.waterism.modelbase.PredictBase;
import tech.waterism.modelbase.PredictResult;
import com.alibaba.fastjson.JSON;
import org.apache.log4j.Logger;
import tech.waterism.model.dto.ModelOutputSimple;
import tech.waterism.model.util.ModelOutputHelper;

import java.util.*;

/**
 * 大伙房水文模型（产汇流模型）
 * 
 * <p>该模型基于大伙房流域的水文特性，实现流域的产流和汇流计算。
 * 主要包括：表层和下层蓄水量计算、蒸散发计算、净雨计算、产流分离、汇流计算等。</p>
 *
 * @author 初京刚
 * @version 2.0
 * @since 2022-01-07
 */
public class DHF extends PredictBase {

    private static final Logger logger = Logger.getLogger(DHF.class);
    
    /** 圆周率 */
    private static final double PI = Math.PI;

    // ==================== 模型参数 ====================
    
    /** 上层蓄水容量 (mm) */
    private double s0;
    
    /** 下层蓄水容量 (mm) */
    private double u0;
    
    /** 地下水蓄水容量，即下层蓄水容量与地下水库蓄水容量之和 (mm) */
    private double d0;
    
    /** 流域蒸散发折算系数 */
    private double Kc;
    
    /** yL与RL的比值 */
    private double Kw;
    
    /** 下层下渗曲线的曲率系数 */
    private double k2;
    
    /** 不透水面积比例 */
    private double g;
    
    /** 表层蓄水容量空间分布曲线形状参数 */
    private double a;
    
    /** 流域内下渗率抛物线形状系数 */
    private double B;
    
    /** 前期净雨影响衰减系数 */
    private double Ka;
    
    // ==================== 汇流参数 ====================
    
    /** 地表汇流曲线形状参数 */
    private double AA;
    private double DD;
    private double CC;
    
    /** 地表汇流曲线比例系数 */
    private double K3;
    
    /** 地表汇流曲线参数，描述峰现时间 */
    private double COE;
    
    /** 特征河长比例系数 */
    private double B0;
    
    /** 汇流曲线底宽求解指数 */
    private double K0;
    
    /** 地下径流与地面壤中流汇流曲线底宽的比例系数 */
    private double N;
    
    /** 流域最大河长 (km) */
    private double L;
    
    /** 地下汇流曲线形状参数 */
    private double AAL;
    private double DDL;
    private double CCL;
    
    /** 地下汇流曲线比例系数 */
    private double K3L;
    
    /** 地表汇流曲线底宽 (h) */
    private double Tm;
    
    /** 特征河长 (km) */
    private double LB;
    
    /** 预见期起始时间（用于区分热预期和预见期） */
    private String forecastStartTime;
    
    // ==================== 初始状态 ====================
    
    /** 初始流域表层平均蓄水量 (mm) */
    private double sa0;
    
    /** 初始流域下层平均蓄水量 (mm) */
    private double ua0;
    
    /** 初始前期影响雨量 (mm) */
    private double ya0;
    
    // ==================== 状态变量 ====================
    
    /** 表层蓄水量时间序列 (mm) */
    private double[] sa;
    
    /** 下层蓄水量时间序列 (mm) */
    private double[] ua;
    
    /** 前期影响雨量时间序列 (mm) */
    private double[] ya;
    
    /** 地下水库下渗强度时间序列 (mm) */
    private double[] rL;
    
    // ==================== 输入数据 ====================
    
    /** 流域月潜在蒸散发 PET (mm) - Potential Evapotranspiration */
    private double[] ES;
    
    /** 降水时间序列 */
    private Date[] floodTm;
    
    /** 降水量序列 (mm) */
    private double[] floodDrp;
    
    /** 时段蒸发量序列 (mm) */
    private double[] floodE;
    
    /** 降水序列长度 */
    private Integer floodRainRange;
    
    /** 降水时段长 (h) */
    private double timeInterval;
    
    /** 流域面积 (km²) */
    private double Area;
    
    // ==================== 输出结果 ====================
    
    /** 模拟总产流 (mm) */
    private double[] RunoffSim;
    
    /** 模拟总径流 (m³/s) */
    private double[] QSim;
    
    /** 不透水面积产流 (mm) */
    private double[] y0;
    
    /** 净雨 (mm) */
    private double[] PE;
    
    /** 分水源-地面壤中流 (mm) */
    private double[] yu;
    
    /** 分水源-地下径流 (mm) */
    private double[] yL;
    
    /** 地表壤中流与地下径流之和 (mm) */
    private double[] y;
    
    /** 汇流-地面壤中流 (m³/s) */
    private double[] qs;
    
    /** 汇流-地下径流 (m³/s) */
    private double[] ql;
    
    /** 最后生成的标准化输出对象（用于接口返回） */
    private ModelOutputSimple lastOutput;

    /**
     * 构造函数，从参数Map中初始化模型
     *
     * @param data 模型参数Map
     */
    public DHF(Map<String, Object> data) {
        super();
        logger.info("初始化大伙房模型...");
        
        try {
            // 提取initialState和parameters子对象（支持新旧两种格式）
            Map<String, Object> initialState = (Map<String, Object>) data.get("initialState");
            Map<String, Object> parameters = (Map<String, Object>) data.get("parameters");
            
            // 如果是新格式（嵌套结构）
            if (initialState != null && parameters != null) {
                logger.info("检测到新格式JSON（嵌套结构）");
                
                // 从initialState读取初始状态
                sa0 = parseDouble(initialState, "SA0", "初始表层蓄水量");
                ua0 = parseDouble(initialState, "UA0", "初始下层蓄水量");
                ya0 = parseDouble(initialState, "YA0", "初始前期影响雨量");
                
                // 从parameters读取所有参数
                s0 = parseDouble(parameters, "S0", "上层蓄水容量");
                u0 = parseDouble(parameters, "U0", "下层蓄水容量");
                d0 = parseDouble(parameters, "D0", "地下水蓄水容量");
                k2 = parseDouble(parameters, "K2", "下层下渗曲线曲率系数");
                Ka = parseDouble(parameters, "KA", "前期净雨影响衰减系数");
                Kc = parseDouble(parameters, "K", "流域蒸散发折算系数");
                Kw = parseDouble(parameters, "KW", "yL与RL的比值");
                g = parseDouble(parameters, "G", "不透水面积比例");
                a = parseDouble(parameters, "A", "表层蓄水容量分布曲线形状参数");
                B = parseDouble(parameters, "B", "下渗率抛物线形状系数");
                B0 = parseDouble(parameters, "B0", "特征河长比例系数");
                K0 = parseDouble(parameters, "K0", "汇流曲线底宽求解指数");
                N = parseDouble(parameters, "N", "地下地面汇流曲线底宽比例系数");
                L = parseDouble(parameters, "L", "流域最大河长");
                DD = parseDouble(parameters, "DD", "地表汇流曲线形状参数DD");
                CC = parseDouble(parameters, "CC", "地表汇流曲线形状参数CC");
                COE = parseDouble(parameters, "COE", "峰现时间参数");
                DDL = parseDouble(parameters, "DDL", "地下汇流曲线形状参数DDL");
                CCL = parseDouble(parameters, "CCL", "地下汇流曲线形状参数CCL");
                Area = parseDouble(parameters, "F", "流域面积");
                
                // clen 和 start 从顶层读取（不在parameters中）
                timeInterval = parseDouble(data, "clen", "时段长");
                
                // 读取预见期起始时间（可选）
                if (data.containsKey("start")) {
                    forecastStartTime = data.get("start").toString();
                    logger.info("预见期起始时间: " + forecastStartTime);
                }
            } else {
                // 旧格式（扁平化结构）- 向后兼容
                logger.info("检测到旧格式JSON（扁平化结构）");
                
                sa0 = parseDouble(data, "SA0", "初始表层蓄水量");
                ua0 = parseDouble(data, "UA0", "初始下层蓄水量");
                ya0 = parseDouble(data, "YA0", "初始前期影响雨量");
                s0 = parseDouble(data, "S0", "上层蓄水容量");
                u0 = parseDouble(data, "U0", "下层蓄水容量");
                d0 = parseDouble(data, "D0", "地下水蓄水容量");
                k2 = parseDouble(data, "K2", "下层下渗曲线曲率系数");
                Ka = parseDouble(data, "KA", "前期净雨影响衰减系数");
                Kc = parseDouble(data, "K", "流域蒸散发折算系数");
                Kw = parseDouble(data, "KW", "yL与RL的比值");
                g = parseDouble(data, "G", "不透水面积比例");
                a = parseDouble(data, "A", "表层蓄水容量分布曲线形状参数");
                B = parseDouble(data, "B", "下渗率抛物线形状系数");
                B0 = parseDouble(data, "B0", "特征河长比例系数");
                K0 = parseDouble(data, "K0", "汇流曲线底宽求解指数");
                N = parseDouble(data, "N", "地下地面汇流曲线底宽比例系数");
                L = parseDouble(data, "L", "流域最大河长");
                DD = parseDouble(data, "DD", "地表汇流曲线形状参数DD");
                CC = parseDouble(data, "CC", "地表汇流曲线形状参数CC");
                COE = parseDouble(data, "COE", "峰现时间参数");
                DDL = parseDouble(data, "DDL", "地下汇流曲线形状参数DDL");
                CCL = parseDouble(data, "CCL", "地下汇流曲线形状参数CCL");
                Area = parseDouble(data, "F", "流域面积");
                timeInterval = parseDouble(data, "clen", "时段长");
                
                // 读取预见期起始时间（可选）
                if (data.containsKey("start")) {
                    forecastStartTime = data.get("start").toString();
                    logger.info("预见期起始时间: " + forecastStartTime);
                }
            }
            
            // 输入数据 - 解析新的时间序列结构
            parseTimeSeriesData(data);
            
            // 初始化输出数组
            initializeArrays();
            
            logger.info("大伙房模型初始化完成，时段数: " + floodRainRange);
        } catch (Exception e) {
            logger.error("大伙房模型初始化失败", e);
            throw new RuntimeException("模型初始化失败", e);
        }
    }

    /**
     * 解析参数并记录日志
     */
    private double parseDouble(Map<String, Object> data, String key, String desc) {
        Object value = data.get(key);
        double result;
        if (value instanceof Number) {
            result = ((Number) value).doubleValue();
        } else {
            result = Double.parseDouble(value.toString());
        }
        logger.debug(desc + " (" + key + "): " + result);
        return result;
    }

    /**
     * 解析新的时间序列数据结构
     * 
     * <p>支持以下数据格式：</p>
     * <ul>
     *   <li>新格式：rainfallSeries/timeSeries - 包含时间和降雨量的对象数组</li>
     *   <li>站点格式：ptRain - 多站点降雨数据，自动计算流域平均</li>
     *   <li>旧格式：dt + rain - 分别存储时间和降雨量的数组</li>
     * </ul>
     * 
     * <p>同时解析月潜在蒸散发数据（PET - Potential Evapotranspiration）</p>
     * 
     * @param data JSON数据Map，包含降雨时间序列和蒸散发数据
     * @throws RuntimeException 当无法解析降雨数据时抛出
     */
    private void parseTimeSeriesData(Map<String, Object> data) {
        logger.info("解析时间序列数据...");
        
        // 1. 解析主降雨序列 (rainfallSeries，兼容旧名称timeSeries)
        String seriesKey = data.containsKey("rainfallSeries") ? "rainfallSeries" : "timeSeries";
        if (data.containsKey(seriesKey)) {
            List<Map<String, Object>> timeSeriesList = (List<Map<String, Object>>) data.get(seriesKey);
            floodRainRange = timeSeriesList.size();
            
            floodTm = new Date[floodRainRange];
            floodDrp = new double[floodRainRange];
            
            for (int i = 0; i < floodRainRange; i++) {
                Map<String, Object> point = timeSeriesList.get(i);
                
                // 解析时间
                String timeStr = (String) point.get("time");
                try {
                    floodTm[i] = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(timeStr);
                } catch (Exception e) {
                    logger.error("时间解析失败: " + timeStr, e);
                    throw new RuntimeException("时间解析失败", e);
                }
                
                // 解析降雨量
                Object rainObj = point.get("rain");
                floodDrp[i] = rainObj instanceof Number ? ((Number) rainObj).doubleValue() : Double.parseDouble(rainObj.toString());
            }
            
            logger.info("主降雨序列解析完成: " + floodRainRange + " 个时段");
        } else if (data.containsKey("ptRain")) {
            // 如果没有主降雨序列，从ptRain站点数据中提取
            logger.warn("未找到rainfallSeries/timeSeries，从ptRain站点数据中提取...");
            extractTimeSeriesFromStations(data);
        } else {
            // 兼容旧格式
            logger.warn("未找到rainfallSeries/timeSeries和ptRain，尝试解析旧格式...");
            floodTm = parseArray(data, "dt", Date[].class, "降水时间");
            floodDrp = parseArray(data, "rain", double[].class, "降水量");
            if (floodDrp != null) {
                floodRainRange = floodDrp.length;
            } else {
                throw new RuntimeException("无法解析降雨数据：未找到rainfallSeries/timeSeries、ptRain或旧格式的rain字段");
            }
        }
        
        // 2. 解析月潜在蒸散发 (monthlyPET - Potential Evapotranspiration)
        // 优先从parameters中查找（新格式）
        Map<String, Object> parameters = (Map<String, Object>) data.get("parameters");
        Object monthlyPETSource = parameters != null ? parameters.get("monthlyPET") : data.get("monthlyPET");
        
        if (monthlyPETSource != null) {
            List<Map<String, Object>> monthlyList = (List<Map<String, Object>>) monthlyPETSource;
            ES = new double[12];
            
            for (int i = 0; i < Math.min(12, monthlyList.size()); i++) {
                Map<String, Object> monthData = monthlyList.get(i);
                Object petObj = monthData.get("pet");
                ES[i] = petObj instanceof Number ? ((Number) petObj).doubleValue() : Double.parseDouble(petObj.toString());
            }
            
            logger.info("月潜在蒸散发(PET)解析完成: " + ES.length + " 个月");
        } else if (data.containsKey("monthlyEvaporation")) {
            // 兼容旧格式 monthlyEvaporation
            logger.warn("使用旧字段名 monthlyEvaporation，建议使用 monthlyPET");
            List<Map<String, Object>> monthlyList = (List<Map<String, Object>>) data.get("monthlyEvaporation");
            ES = new double[12];
            
            for (int i = 0; i < Math.min(12, monthlyList.size()); i++) {
                Map<String, Object> monthData = monthlyList.get(i);
                Object evapObj = monthData.get("evaporation");
                if (evapObj == null) {
                    evapObj = monthData.get("pet");
                }
                ES[i] = evapObj instanceof Number ? ((Number) evapObj).doubleValue() : Double.parseDouble(evapObj.toString());
            }
            
            logger.info("月潜在蒸散发(PET)解析完成: " + ES.length + " 个月");
        } else if (data.containsKey("ES")) {
            // 兼容最旧格式
            logger.warn("未找到monthlyPET，使用旧格式ES");
            ES = parseArray(data, "ES", double[].class, "月潜在蒸散发");
        }
        
        // 3. 初始化时段蒸发量（如果有的话）
        if (data.containsKey(seriesKey)) {
            List<Map<String, Object>> timeSeriesList = (List<Map<String, Object>>) data.get(seriesKey);
            floodE = new double[floodRainRange];
            
            for (int i = 0; i < floodRainRange; i++) {
                Map<String, Object> point = timeSeriesList.get(i);
                if (point.containsKey("evaporation")) {
                    Object evapObj = point.get("evaporation");
                    floodE[i] = evapObj instanceof Number ? ((Number) evapObj).doubleValue() : Double.parseDouble(evapObj.toString());
                } else {
                    // 如果没有时段蒸发量，设为 -1 标记，后续从月蒸发量计算
                    floodE[i] = -1.0;
                }
            }
        } else if (data.containsKey("evaporation")) {
            // 兼容旧格式
            floodE = parseArray(data, "evaporation", double[].class, "时段蒸发量");
        } else {
            floodE = new double[floodRainRange];
            Arrays.fill(floodE, 0.0);
        }
        
        logger.info("时间序列数据解析完成");
    }

    /**
     * 从多站点降雨数据中提取流域平均降雨时间序列
     * 
     * <p>计算方法：</p>
     * <ol>
     *   <li>从第一个站点提取时间序列</li>
     *   <li>计算各站点在每个时段的降雨量算术平均值</li>
     *   <li>作为流域平均降雨量（简化处理，实际应使用泰森多边形权重）</li>
     * </ol>
     * 
     * @param data JSON数据Map，包含ptRain站点数组
     * @throws RuntimeException 当ptRain数据为空或格式错误时抛出
     */
    private void extractTimeSeriesFromStations(Map<String, Object> data) {
        List<Map<String, Object>> ptRainList = (List<Map<String, Object>>) data.get("ptRain");
        
        if (ptRainList == null || ptRainList.isEmpty()) {
            throw new RuntimeException("ptRain数据为空");
        }
        
        // 使用第一个站点的rainSeries提取时间序列
        Map<String, Object> firstStation = ptRainList.get(0);
        List<Map<String, Object>> rainSeriesList = (List<Map<String, Object>>) firstStation.get("rainSeries");
        
        if (rainSeriesList == null || rainSeriesList.isEmpty()) {
            throw new RuntimeException("站点rainSeries数据为空");
        }
        
        floodRainRange = rainSeriesList.size();
        floodTm = new Date[floodRainRange];
        floodDrp = new double[floodRainRange];
        
        // 计算流域平均降雨量（泰森多边形加权平均）
        for (int i = 0; i < floodRainRange; i++) {
            // 解析时间（从第一个站点）
            Map<String, Object> firstPoint = rainSeriesList.get(i);
            String timeStr = (String) firstPoint.get("time");
            try {
                floodTm[i] = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(timeStr);
            } catch (Exception e) {
                logger.error("时间解析失败: " + timeStr, e);
                throw new RuntimeException("时间解析失败", e);
            }
            
            // 计算各站点降雨量的算术平均值（简化处理，实际应该用泰森多边形权重）
            double sumRain = 0.0;
            int validStations = 0;
            
            for (Map<String, Object> station : ptRainList) {
                List<Map<String, Object>> stationRainSeries = (List<Map<String, Object>>) station.get("rainSeries");
                if (stationRainSeries != null && i < stationRainSeries.size()) {
                    Map<String, Object> point = stationRainSeries.get(i);
                    Object rainObj = point.get("rain");
                    if (rainObj != null) {
                        double rain = rainObj instanceof Number ? ((Number) rainObj).doubleValue() : Double.parseDouble(rainObj.toString());
                        sumRain += rain;
                        validStations++;
                    }
                }
            }
            
            // 取平均值作为流域降雨量
            floodDrp[i] = validStations > 0 ? sumRain / validStations : 0.0;
        }
        
        logger.info("从" + ptRainList.size() + "个站点提取时间序列并计算流域平均降雨量: " + floodRainRange + " 个时段");
    }

    /**
     * 解析数组字段 - 兼容字符串和直接的JSON数组
     * 
     * <p>支持两种格式：</p>
     * <ul>
     *   <li>旧格式：字符串形式的JSON数组，需要先解析</li>
     *   <li>新格式：已经是JSON对象，直接转换</li>
     * </ul>
     * 
     * @param <T> 目标类型
     * @param data 数据Map
     * @param key 字段键名
     * @param clazz 目标类型Class
     * @param desc 字段描述（用于日志）
     * @return 解析后的数组对象
     */
    private <T> T parseArray(Map<String, Object> data, String key, Class<T> clazz, String desc) {
        Object value = data.get(key);
        T result;
        if (value instanceof String) {
            // 旧格式：字符串形式的JSON数组
            result = JSON.parseObject((String) value, clazz);
        } else {
            // 新格式：已经是JSON对象
            result = JSON.parseObject(JSON.toJSONString(value), clazz);
        }
        logger.debug(desc + " (" + key + "): 已解析，长度=" + 
                    (result != null && result.getClass().isArray() ? 
                     java.lang.reflect.Array.getLength(result) : "unknown"));
        return result;
    }

    /**
     * 初始化输出数组
     * 
     * <p>为所有输出变量分配内存空间，数组长度为时段数 floodRainRange</p>
     * 
     * <p>输出变量包括：</p>
     * <ul>
     *   <li>RunoffSim - 总产流 (mm)</li>
     *   <li>QSim - 总流量 (m³/s)</li>
     *   <li>y0 - 不透水面积产流 (mm)</li>
     *   <li>PE - 净雨 (mm)</li>
     *   <li>yu - 地面壤中流 (mm)</li>
     *   <li>yL - 地下径流 (mm)</li>
     *   <li>y - 地表壤中流与地下径流之和 (mm)</li>
     *   <li>qs - 地面壤中流流量 (m³/s)</li>
     *   <li>ql - 地下径流流量 (m³/s)</li>
     *   <li>rL - 地下水库下渗强度 (mm)</li>
     * </ul>
     */
    private void initializeArrays() {
        RunoffSim = new double[floodRainRange];
        QSim = new double[floodRainRange];
        y0 = new double[floodRainRange];
        PE = new double[floodRainRange];
        yu = new double[floodRainRange];
        yL = new double[floodRainRange];
        y = new double[floodRainRange];
        rL = new double[floodRainRange];
        qs = new double[floodRainRange];
        ql = new double[floodRainRange];
    }

    /**
     * 测试入口 - 使用转换后的标准JSON文件
     */
    public static void main(String[] args) {
        logger.info("开始大伙房模型测试...");
        logger.info("读取转换后的JSON文件: json/DHF-input.json");
        
        // 读取转换后的标准JSON文件
        String content = ResourceUtil.readUtf8Str("json/DHF-input.json");
        Map<String, Object> data = JSON.parseObject(content, Map.class);
        
        DHF model = new DHF(data);
        PredictResult result = model.predict();
        
        logger.info("模型计算完成");
        logger.info("输出结果: " + JSON.toJSONString(result));
        System.out.println(JSON.toJSONString(result));
    }

    @Override
    public void init() {
        // 初始化已在构造函数中完成
    }

    @Override
    public PredictResult predict() {
        logger.info("开始大伙房模型预测计算...");
        
        PredictResult result = new PredictResult();
        Map<String, double[]> map = runModel();
        
        // ===== 传统数组格式输出（向后兼容）=====
        // 分水源产流结果
        double[][] RSim = new double[4][];
        RSim[0] = map.get("y0");  // 不透水面积产流
        RSim[1] = map.get("yu");  // 流域地表壤中流
        RSim[2] = map.get("yL");  // 流域地下径流
        RSim[3] = map.get("y");   // 流域地表壤中流与地下径流之和
        
        // 总产流
        result.setRunoffSim(map.get("runoffSim"));
        result.setRSim(RSim);
        
        // 汇流计算结果
        result.setQSim(map.get("QSim"));
        
        // 时间序列
        result.setDt(floodTm);
        result.setRain(floodDrp);
        
        // 更新状态
        Map<String, List<String>> status = new HashMap<>();
        status.put("SA0", toStringList(sa));
        status.put("UA0", toStringList(ua));
        status.put("YA0", toStringList(ya));
        result.setNewStatus(status);
        
        // ===== 新增：扁平化输出并保存到文件 =====
        try {
            ModelOutputHelper helper = new ModelOutputHelper("DHF", "1.0");
            
            // 计算预热期时段数
            int warmUpPeriods = calculateWarmUpPeriods();
            
            // 设置时间序列和预热期
            helper.setTimeStamps(floodTm)
                  .setWarmUpPeriods(warmUpPeriods);
            
            // 添加所有水文要素（扁平化数组）
            helper.addDataArray("rain", map.get("rain"))
                  .addDataArray("PE", map.get("PE"))
                  .addDataArray("runoffSim", map.get("runoffSim"))
                  .addDataArray("y0", map.get("y0"))
                  .addDataArray("yu", map.get("yu"))
                  .addDataArray("yL", map.get("yL"))
                  .addDataArray("y", map.get("y"))
                  .addDataArray("QSim", map.get("QSim"))
                  .addDataArray("qs", map.get("qs"))
                  .addDataArray("ql", map.get("ql"));
            
            // 添加状态变量时间序列
            helper.addStateArray("SA0", sa)
                  .addStateArray("UA0", ua)
                  .addStateArray("YA0", ya);
            
            // 设置最终状态
            Map<String, Object> finalState = new HashMap<>();
            finalState.put("SA0", sa[sa.length - 1]);
            finalState.put("UA0", ua[ua.length - 1]);
            finalState.put("YA0", ya[ya.length - 1]);
            helper.setFinalState(finalState);
            
            // 设置模型参数
            Map<String, Double> params = new HashMap<>();
            params.put("S0_表层蓄水容量", s0);
            params.put("U0_下层蓄水容量", u0);
            params.put("Ka_前期影响雨量消退系数", Ka);
            params.put("G_不透水面积比", g);
            params.put("L_流域最大河长", L);
            helper.setModelParameters(params);
            
            // 设置元数据
            Map<String, Object> metadata = new HashMap<>();
            metadata.put("area_km2", Area);
            metadata.put("timestep_hours", timeInterval);
            helper.setMetadata(metadata);
            
            // 构建并保存到文件
            ModelOutputSimple output = helper.build();
            this.lastOutput = output;  // 保存输出对象供接口使用
            String outputPath = "output/dhf_output.json";
            ModelOutputHelper.saveToFile(output, outputPath);
            logger.info("✅ DHF模型输出已保存到: " + outputPath);
        } catch (Exception e) {
            logger.error("保存模型输出时出错: " + e.getMessage(), e);
        }
        
        logger.info("大伙房模型预测计算完成");
        return result;
    }
    
    /**
     * 获取最后生成的标准化输出对象
     * 
     * <p>该方法用于接口返回，提供与output文件一致的格式</p>
     * 
     * @return ModelOutputSimple对象，包含完整的模型输出信息
     */
    public ModelOutputSimple getLastOutput() {
        return lastOutput;
    }
    
    /**
     * 计算预热期时段数
     * 
     * <p>根据预见期起始时间（start）在时间序列中查找对应的位置，
     * 该位置之前的时段作为预热期（warm-up period），之后的作为预见期（forecast period）</p>
     * 
     * @return 预热期时段数，如果未指定start则返回默认值（总时段数>72返回72，否则返回0）
     */
    private int calculateWarmUpPeriods() {
        if (forecastStartTime == null || forecastStartTime.isEmpty()) {
            // 如果没有指定start，使用默认策略
            int defaultWarmUp = floodRainRange > 72 ? 72 : 0;
            logger.info("未指定预见期起始时间，使用默认预热期: " + defaultWarmUp + " 时段");
            return defaultWarmUp;
        }
        
        try {
            // 解析预见期起始时间
            java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date startDate = sdf.parse(forecastStartTime);
            
            // 在时间序列中查找对应位置
            for (int i = 0; i < floodTm.length; i++) {
                if (floodTm[i].equals(startDate) || floodTm[i].after(startDate)) {
                    logger.info("预见期起始于第 " + i + " 时段（时间: " + sdf.format(floodTm[i]) + "），预热期: " + i + " 时段");
                    return i;
                }
            }
            
            // 如果start晚于所有时间，则全部作为预热期
            logger.warn("预见期起始时间 " + forecastStartTime + " 晚于所有时段，全部视为预热期");
            return floodRainRange;
            
        } catch (Exception e) {
            logger.error("解析预见期起始时间失败: " + forecastStartTime + "，使用默认策略", e);
            return floodRainRange > 72 ? 72 : 0;
        }
    }

    /**
     * 运行大伙房模型主流程
     * 
     * <p>计算流程：</p>
     * <ol>
     *   <li>产流计算 - 计算表层/下层蓄水量、蒸散发、净雨、径流分离</li>
     *   <li>更新前期影响雨量 - 根据当前时段产流量更新YA</li>
     *   <li>汇流计算 - 使用瞬时单位线法计算河道汇流</li>
     * </ol>
     * 
     * <p>物理过程：</p>
     * <pre>
     * 降雨 → 蒸散发 → 净雨 → 产流（地表+地下）→ 汇流 → 河道流量
     * </pre>
     *
     * @return 模拟结果Map，包含以下键值：
     *         <ul>
     *           <li>rain - 降雨量序列 (mm)</li>
     *           <li>PE - 净雨序列 (mm)</li>
     *           <li>runoffSim - 总产流序列 (mm)</li>
     *           <li>y0 - 不透水面积产流 (mm)</li>
     *           <li>yu - 地面壤中流 (mm)</li>
     *           <li>yL - 地下径流 (mm)</li>
     *           <li>y - 地表壤中流与地下径流之和 (mm)</li>
     *           <li>qs - 地面壤中流流量 (m³/s)</li>
     *           <li>ql - 地下径流流量 (m³/s)</li>
     *           <li>QSim - 总流量 (m³/s)</li>
     *         </ul>
     */
    public Map<String, double[]> runModel() {
        logger.info("开始产流计算...");
        runoffGeneration();
        
        // 计算总径流和更新前期影响雨量（必须在汇流计算之前）
        for (int j = 0; j < floodRainRange; j++) {
            RunoffSim[j] = y[j] + y0[j]; // 总径流
            if (RunoffSim[j] < 0.0) {
                RunoffSim[j] = 0.0;
            }
            rL[j] = yL[j]; // 地下水库下渗强度
            if (rL[j] < 0.0) {
                rL[j] = 0.0;
            }
            
            // 更新前期影响雨量
            if (j + 1 < floodRainRange + 1) {
                ya[j + 1] = (ya[j] + RunoffSim[j]) * Ka;
                if (ya[j + 1] < 0.0) {
                    ya[j + 1] = 0.0;
                }
            }
        }
        
        logger.info("开始汇流计算...");
        flowRouting();
        
        Map<String, double[]> result = new HashMap<>();
        result.put("rain", floodDrp);
        result.put("PE", PE);
        result.put("runoffSim", RunoffSim);
        result.put("y0", y0);
        result.put("yu", yu);
        result.put("yL", yL);
        result.put("y", y);
        result.put("qs", qs);
        result.put("ql", ql);
        result.put("QSim", QSim);
        
        return result;
    }

    /**
     * 产流计算 - 大伙房模型核心产流算法
     * 
     * <p>采用两层蓄水结构：</p>
     * <ul>
     *   <li>表层蓄水（SA）- 响应快，控制地表产流</li>
     *   <li>下层蓄水（UA）- 响应慢，控制地下产流</li>
     * </ul>
     * 
     * <p>物理过程：</p>
     * <pre>
     * 1. 蒸散发计算 - 从月PET计算时段蒸散发
     * 2. 净雨计算 - 降雨减去蒸散发
     * 3. 不透水面积产流 - 直接按比例产流
     * 4. 透水面积产流：
     *    - 超渗产流机制
     *    - 分离为地面壤中流（yu）和地下径流（yL）
     * 5. 更新蓄水状态 - SA和UA的演变
     * </pre>
     * 
     * <p>状态变量更新：</p>
     * <ul>
     *   <li>sa[0..n] - 各时段表层蓄水量 (mm)</li>
     *   <li>ua[0..n] - 各时段下层蓄水量 (mm)</li>
     *   <li>ya[0..n] - 各时段前期影响雨量 (mm)</li>
     * </ul>
     */
    private void runoffGeneration() {
        sa = new double[floodRainRange + 1];
        ua = new double[floodRainRange + 1];
        ya = new double[floodRainRange + 1];
        
        // 设置初始状态
        sa[0] = Math.min(sa0, s0);
        ua[0] = Math.min(ua0, u0);
        ya[0] = ya0;
        
        logger.debug("初始状态 - SA0: " + sa[0] + ", UA0: " + ua[0] + ", YA0: " + ya[0]);
        
        // 逐时段计算
        for (int i = 0; i < floodRainRange; i++) {
            calculateTimeStep(i);
        }
        
        logger.debug("产流计算完成");
    }

    /**
     * 单时段产流计算
     *
     * <p>计算步骤：</p>
     * <ol>
     *   <li>计算时段蒸散发量</li>
     *   <li>计算净雨和不透水面积产流</li>
     *   <li>判断净雨正负：</li>
     *   <ul>
     *     <li>净雨>0：降雨超渗产流过程</li>
     *     <li>净雨≤0：蒸发亏缺过程</li>
     *   </ul>
     *   <li>更新下一时段的蓄水状态</li>
     * </ol>
     *
     * @param i 时段索引（0-based）
     */
    private void calculateTimeStep(int i) {
        // 确保状态值在合理范围内
        sa[i] = Math.min(sa[i], s0);
        ua[i] = Math.min(ua[i], u0);
        
        // 初始化
        yu[i] = 0.0;
        yL[i] = 0.0;
        
        // 计算蒸散发
        double EDt = calculateEvapotranspiration(i);
        
        // 计算净雨和不透水面积产流
        PE[i] = floodDrp[i] - EDt;
        y0[i] = g * PE[i];
        double Pc = PE[i] - y0[i];  // 净渗雨强
        
        if (Pc > 0.0) {
            // 降雨大于蒸发
            calculateRunoffExcess(i, Pc);
        } else {
            // 蒸发大于降雨
            calculateEvaporationDeficit(i, EDt);
        }
        
        // 确保下一时段状态值在合理范围内
        if (i + 1 < floodRainRange + 1) {
            sa[i + 1] = Math.max(0.0, Math.min(sa[i + 1], s0));
            ua[i + 1] = Math.max(0.0, Math.min(ua[i + 1], u0));
        }
    }

    /**
     * 计算时段实际蒸散发量（ET - Evapotranspiration）
     * 
     * <p>计算方法：</p>
     * <ul>
     *   <li>优先使用实际观测的时段蒸发量（如果提供）</li>
     *   <li>否则从月潜在蒸散发（PET）按时段长度换算</li>
     * </ul>
     * 
     * <p>公式：ET = Kc × PET</p>
     * <ul>
     *   <li>Kc - 流域蒸散发折算系数（考虑植被、土壤等因素）</li>
     *   <li>PET - 潜在蒸散发量（Potential Evapotranspiration）</li>
     * </ul>
     *
     * @param i 时段索引（0-based）
     * @return 时段实际蒸散发量 (mm)
     */
    private double calculateEvapotranspiration(int i) {
        if (floodE != null && floodE.length == floodRainRange && floodE[i] >= 0) {
            // 使用实际的时段蒸发量
            return Kc * floodE[i];
        } else {
            // 从月蒸发量计算时段蒸发量
            return Kc * calcEvaporationPotential(floodTm[i], timeInterval);
        }
    }

    /**
     * 计算降雨超渗产流（超渗-蓄满混合产流机制）
     * 
     * <p>物理过程：</p>
     * <pre>
     * 降雨 → 蒸散发 → 净雨 → 表层蓄水入渗 → 超渗径流
     *                              ↓
     *                          下层蓄水入渗 → 分离为地面壤中流（yu）和地下径流（yL）
     * </pre>
     * 
     * <p>关键步骤：</p>
     * <ol>
     *   <li>计算表层相应蓄水量Sm - 考虑蓄水容量分布曲线</li>
     *   <li>计算下渗强度rr - 净雨中不能被表层吸收的部分</li>
     *   <li>计算下层入渗分配 - 考虑下层蓄水容量曲线</li>
     *   <li>径流分离：</li>
     *   <ul>
     *     <li>y  - 地表壤中流与地下径流之和</li>
     *     <li>yu - 地面壤中流（快速响应）</li>
     *     <li>yL - 地下径流（慢速响应）= (y - yu) × Kw</li>
     *   </ul>
     *   <li>更新蓄水状态：sa[i+1], ua[i+1]</li>
     * </ol>
     * 
     * <p>参数说明：</p>
     * <ul>
     *   <li>a - 表层蓄水容量分布曲线形状参数</li>
     *   <li>B - 下渗率抛物线形状系数</li>
     *   <li>k2 - 下层下渗曲线曲率系数</li>
     *   <li>Kw - yL与RL的比值</li>
     * </ul>
     *
     * @param i  时段索引（0-based）
     * @param Pc 净渗雨强，即扣除不透水面积后的净雨 (mm)
     */
    private void calculateRunoffExcess(int i, double Pc) {
        // 计算相应于Sa的点蓄水量
        double temp = Math.pow(1 - sa[i] / s0, 1 / a);
        double Sm = a * s0 * (1 - temp);
        
        // 计算下渗强度
        double rr;
        if (Sm + Pc < a * s0) {
            temp = Math.pow(1 - (Sm + Pc) / (a * s0), a);
            rr = Pc + sa[i] - s0 + s0 * temp;
        } else {
            rr = Pc - (s0 - sa[i]);
        }
        
        // 计算下层相关参数
        temp = Math.pow(1 - ua[i] / u0, 1 / B);
        double un = B * u0 * (1 - temp);
        
        temp = Math.pow(1 - ua[i] / u0, u0 / (B * d0));
        double dn = B * d0 * (1 - temp);
        
        double Z1 = 1 - Math.exp(-k2 * timeInterval * u0 / d0);
        double Z2 = 1 - Math.exp(-k2 * timeInterval);
        
        // 计算地面壤中流和地下径流之和
        if (rr + Z2 * un < Z2 * B * u0) {
            temp = Math.pow(1 - (Z2 * un + rr) / (Z2 * B * u0), B);
            y[i] = rr + Z2 * (ua[i] - u0) + Z2 * u0 * temp;
        } else {
            y[i] = rr + Z2 * (ua[i] - u0);
        }
        
        // 计算地面壤中流
        temp = Math.pow(1 - ua[i] / u0, u0 / d0);
        if (Z1 * dn + rr < Z1 * B * d0) {
            double temp1 = 1 - (Z1 * dn + rr) / (Z1 * B * d0);
            double temp2 = Math.pow(temp1, B);
            yu[i] = rr - Z1 * d0 * temp + Z1 * d0 * temp2;
        } else {
            yu[i] = rr - Z1 * d0 * temp;
        }
        
        // 计算地下径流
        yL[i] = (y[i] - yu[i]) * Kw;
        
        // 更新表层蓄水量
        if (Sm + Pc < a * s0) {
            double temp1 = 1 - (Sm + Pc) / (a * s0);
            double temp2 = Math.pow(temp1, a);
            sa[i + 1] = s0 * (1 - temp2);
        } else {
            sa[i + 1] = sa[i] + Pc - rr;
        }
        
        // 更新下层蓄水量
        ua[i + 1] = ua[i] + rr - y[i];
    }

    /**
     * 计算蒸发亏缺情况（降雨小于蒸散发）
     * 
     * <p>当 PE < 0 时（即蒸散发 > 降雨），土壤水分蒸发过程：</p>
     * <ol>
     *   <li>优先从表层蓄水（SA）中蒸发</li>
     *   <li>表层不足时，从下层蓄水（UA）中蒸发</li>
     *   <li>蒸发量与土壤含水量成正比</li>
     * </ol>
     * 
     * <p>物理意义：</p>
     * <ul>
     *   <li>Ec - 蒸发亏缺量，需从土壤中提取的水量</li>
     *   <li>Eu - 表层可蒸发量，考虑蓄水容量分布曲线</li>
     *   <li>EL - 下层蒸发量，按下层含水率比例计算</li>
     * </ul>
     * 
     * <p>无产流发生，所有径流分量置零</p>
     *
     * @param i   时段索引（0-based）
     * @param EDt 时段蒸散发能力/潜在蒸散发量 (mm)
     */
    private void calculateEvaporationDeficit(int i, double EDt) {
        double Ec = EDt - floodDrp[i];
        double Eb = Ec;
        
        // 计算表层可蒸发量
        double Eu;
        if (Eb / (a * s0) <= 0.999999 && (Eb - Ec) / (a * s0) <= 0.999999) {
            double temp1 = Math.pow(1 - (Eb - Ec) / (a * s0), a);
            double temp2 = Math.pow(1 - Eb / (a * s0), a);
            Eu = s0 * (temp1 - temp2);
        } else if (Eb / (a * s0) >= 1.00001 && (Eb - Ec) / (a * s0) <= 0.999999) {
            double temp1 = Math.pow(1 - (Eb - Ec) / (a * s0), a);
            Eu = s0 * temp1;
        } else {
            Eu = 0.00001;
        }
        
        // 更新蓄水量
        if (i + 1 < floodRainRange + 1) {
            if (sa[i] - Eu < 0.0) {
                double EL = (Ec - sa[i]) * ua[i] / u0;
                sa[i + 1] = 0.0;
                ua[i + 1] = Math.max(0.0, ua[i] - EL);
            } else {
                double EL = (Ec - Eu) * ua[i] / u0;
                sa[i + 1] = sa[i] - Eu;
                ua[i + 1] = Math.max(0.0, ua[i] - EL);
            }
        }
        
        // 无径流产生
        y[i] = 0.0;
        y0[i] = 0.0;
        yu[i] = 0.0;
        yL[i] = 0.0;
    }

    /**
     * 河道汇流计算 - 采用瞬时单位线法（IUH - Instantaneous Unit Hydrograph）
     * 
     * <p>汇流原理：</p>
     * <ul>
     *   <li>地表径流和地下径流采用不同的汇流曲线</li>
     *   <li>汇流曲线形状随前期影响雨量（YA）和当前产流量动态调整</li>
     *   <li>汇流曲线底宽（Tm）反映流域响应时间</li>
     * </ul>
     * 
     * <p>计算步骤：</p>
     * <ol>
     *   <li>计算单位转换系数 w0 = F/(3.6×Δt)</li>
     *   <li>逐时段计算汇流曲线底宽 Tm</li>
     *   <li>计算地下汇流参数（AAL, K3L）</li>
     *   <li>计算地表汇流参数（AA, K3）</li>
     *   <li>进行汇流演算，叠加各时段贡献</li>
     * </ol>
     * 
     * <p>汇流分量：</p>
     * <ul>
     *   <li>qs[i] - 地表壤中流流量 (m³/s)</li>
     *   <li>ql[i] - 地下径流流量 (m³/s)</li>
     *   <li>QSim[i] - 总流量 = qs + ql (m³/s)</li>
     * </ul>
     * 
     * <p>关键参数：</p>
     * <ul>
     *   <li>L - 流域最大河长 (km)</li>
     *   <li>B0 - 特征河长比例系数</li>
     *   <li>K0 - 汇流曲线底宽求解指数</li>
     *   <li>N - 地下/地表汇流曲线底宽比</li>
     *   <li>COE - 峰现时间参数</li>
     *   <li>DD, CC - 地表汇流曲线形状参数</li>
     *   <li>DDL, CCL - 地下汇流曲线形状参数</li>
     * </ul>
     */
    private void flowRouting() {
        double w0 = Area / (3.6 * timeInterval);  // 单位转换系数
        
        for (int i = 0; i < floodRainRange; i++) {
            // 防止前期影响雨量过小
            if (ya[i] < 0.5) {
                ya[i] = 0.5;
            }
            
            // 计算汇流曲线底宽
            double tempTm = Math.pow(ya[i] + RunoffSim[i], -K0);
            LB = L / B0;
            Tm = LB * tempTm;
            
            int TT = (int) (N * Tm);  // 地下径流汇流曲线宽度
            int TS = (int) (COE * Tm);  // 地表峰现时间
            
            // 计算地下汇流参数
            calculateUndergroundFlowParameters(TT, w0);
            
            // 计算地表汇流参数
            calculateSurfaceFlowParameters(w0);
            
            // 汇流演算
            routeFlow(i, TT, TS);
        }
        
        logger.debug("汇流计算完成");
    }

    /**
     * 计算地下汇流参数
     * 
     * <p>地下径流汇流曲线采用修正的伽马分布形式：</p>
     * <pre>
     * U(t) = K3L/TT × exp(-AAL × (πt/TT)^DDL) × sin^CCL(πt/TT)
     * </pre>
     * 
     * <p>计算步骤：</p>
     * <ol>
     *   <li>计算形状参数 AAL = CCL/(DDL × (πCOE/N)^(DDL-1) × tan(πCOE/N))</li>
     *   <li>数值积分求和，计算归一化系数 K3L</li>
     * </ol>
     *
     * @param TT 地下径流汇流曲线底宽（时段数）
     * @param w0 单位转换系数 = F/(3.6×Δt)，用于径流深转流量
     */
    private void calculateUndergroundFlowParameters(int TT, double w0) {
        double tempAAL = Math.pow(PI * COE / N, DDL - 1);
        AAL = CCL / (DDL * tempAAL * Math.tan(PI * COE / N));
        
        K3L = 0.0;
        for (int j = 0; j < TT; j++) {
            double tmp = Math.pow(PI * j / TT, DDL);
            double tmp1 = Math.pow(Math.sin(PI * j / TT), CCL);
            K3L += Math.exp(-AAL * tmp) * tmp1;
        }
        K3L = TT * w0 / K3L;
    }

    /**
     * 计算地表汇流参数
     * 
     * <p>地表径流汇流曲线采用修正的伽马分布形式：</p>
     * <pre>
     * U(t) = K3/Tm × exp(-AA × (πt/Tm)^DD) × sin^CC(πt/Tm)
     * </pre>
     * 
     * <p>计算步骤：</p>
     * <ol>
     *   <li>计算形状参数 AA = CC/(DD × (πCOE)^(DD-1) × tan(πCOE))</li>
     *   <li>数值积分求和，计算归一化系数 K3</li>
     * </ol>
     *
     * @param w0 单位转换系数 = F/(3.6×Δt)，用于径流深转流量
     */
    private void calculateSurfaceFlowParameters(double w0) {
        double tempAA = Math.pow(PI * COE, DD - 1);
        AA = CC / (DD * tempAA * Math.tan(PI * COE));
        
        K3 = 0.0;
        for (int j = 0; j < Tm; j++) {
            double tmp = Math.pow(PI * j / Tm, DD);
            double tmp1 = Math.pow(Math.sin(PI * j / Tm), CC);
            K3 += Math.exp(-AA * tmp) * tmp1;
        }
        K3 = Tm * w0 / K3;
    }

    /**
     * 汇流演算 - 将单位时段产流转换为河道流量
     * 
     * <p>汇流卷积过程：</p>
     * <pre>
     * Q(t) = Σ [R(τ) × U(t-τ)]
     * </pre>
     * 其中：R(τ)为τ时刻的产流，U(t-τ)为单位线响应
     * 
     * <p>分水源汇流：</p>
     * <ul>
     *   <li>地表径流（RunoffSim - rL）：响应快，峰现时间早（TS）</li>
     *   <li>地下径流（rL）：响应慢，峰现时间晚，底宽较宽（TT）</li>
     * </ul>
     * 
     * <p>时间偏移：</p>
     * <ul>
     *   <li>TL = TT + TS - 1：总汇流过程长度</li>
     *   <li>TS：地下径流相对地表径流的滞后时间</li>
     * </ul>
     *
     * @param i  当前时段索引
     * @param TT 地下径流汇流曲线底宽（时段数）
     * @param TS 地下径流相对地表径流的滞后时段数
     */
    private void routeFlow(int i, int TT, int TS) {
        int TL = TT + TS - 1;
        if (TL <= 0) {
            TL = 0;
        }
        
        for (int j = 0; j < TL; j++) {
            if (i + j >= QSim.length) {
                break;
            }
            
            // 地表汇流
            if (j <= Tm) {
                double temp0 = PI * j / Tm;
                double temp1 = Math.pow(temp0, DD);
                double temp2 = Math.exp(-AA * temp1);
                double temp3 = Math.pow(Math.sin(temp0), CC);
                double Qs = (RunoffSim[i] - rL[i]) * K3 / Tm * temp2 * temp3;
                
                if (Double.isNaN(Qs)) {
                    Qs = 0;
                }
                
                if (j > TS) {
                    qs[i + j] += Qs;
                } else {
                    qs[i + j] += Qs;
                }
            }
            
            // 地下汇流
            if (j > TS) {
                double temp00 = PI * (j - TS) / TT;
                double temp10 = Math.pow(temp00, DDL);
                double temp20 = Math.exp(-AAL * temp10);
                double temp30 = Math.pow(Math.sin(temp00), CCL);
                double Ql = rL[i] * K3L / TT * temp20 * temp30;
                
                ql[i + j] += Ql;
            }
            
            QSim[i + j] = qs[i + j] + ql[i + j];
            if (QSim[i + j] < 0.0) {
                QSim[i + j] = 0.0;
            }
        }
    }

    /**
     * 计算时段潜在蒸散发量（PET - Potential Evapotranspiration）
     * 
     * <p>从月潜在蒸散发量换算为时段蒸散发量：</p>
     * <pre>
     * 月PET → 日PET → 时段PET
     * 日PET = 月PET / 30
     * 时段PET = 日PET × (Δt / 24)
     * </pre>
     * 
     * <p>假设：</p>
     * <ul>
     *   <li>每月按30天计算（简化）</li>
     *   <li>蒸散发在一天内均匀分布</li>
     *   <li>月内蒸散发能力恒定</li>
     * </ul>
     *
     * @param date   当前时段时间
     * @param timeIn 时段长度 (h)
     * @return 时段潜在蒸散发量 (mm)
     */
    private double calcEvaporationPotential(Date date, double timeIn) {
        Calendar time = Calendar.getInstance();
        time.setTime(date);
        int month = time.get(Calendar.MONTH) + 1;
        
        double Ep = ES[month - 1] / 30.0;  // 月PET转换成日PET
        Ep = Ep * timeIn / 24.0;           // 日PET转换成时段PET
        
        return Ep;
    }

    /**
     * 将double数组转换为String列表
     * 
     * <p>用于将数值型状态变量转换为字符串格式，便于JSON序列化和存储</p>
     *
     * @param array double数组
     * @return String列表
     */
    private List<String> toStringList(double[] array) {
        List<String> list = new ArrayList<>(array.length);
        for (double v : array) {
            list.add(String.valueOf(v));
        }
        return list;
    }
}

